#include <assert.h>
#include <stdio.h>
#include <stdlib.h>

#include "../src/prating.c"

typedef struct _LogPRatingTestDatum LogPRatingTestDatum ;
struct _LogPRatingTestDatum 
{ 
  struct
    {
      unsigned int      rating;
      float            loga[5];
      float            logbeta;
      float            c[5];
      float            d[5 * 4];
    }                                   inputs;

  struct
    {
      float     logp;
      float     dlogp;
      float     ddlogp;
    }                                   desired_output;
};

static void
test_logprating (void)
{
  LogPRatingTestDatum data[] = 
    {{{1, {0.664458, 0.0555755, 0.758868, 0.554894, 0.212639},   0.468396, {0.303691, 0.8673, 0.293263, 0.0122363,    0.406185}, {0.0687761, 0.892649, 0.683659, 0.927809, 0.206677,    0.967, 0.740127, 0.76364, 0.445148, 0.191141, 0.317757, 0.441397,    0.18739, 0.526683, 0.262181, 0.682529, 0.632496, 0.314044,    0.793785,    0.378838}}, {-1.46275, -0.0844507, -0.101061}}, {{2, {0.765196,    0.020781, 0.781549, 0.972653, 0.69642},   0.128132, {0.0978893, 0.0448435, 0.489742, 0.161132,    0.357763}, {0.281203, 0.0445945, 0.969991, 0.0400057, 0.839806,    0.857205, 0.443308, 0.777824, 0.157277, 0.224709, 0.129264,    0.984039, 0.778439, 0.459514, 0.108483, 0.202491, 0.805786,    0.763094, 0.980351, 0.104601}}, {-1.37914, 0.00698172,   0.00662699}}, {{2, {0.760943, 0.273352, 0.819219, 0.746839,    0.47974},   0.228757, {0.849228, 0.706833, 0.639934, 0.371552,    0.40592}, {0.929009, 0.482656, 0.146843, 0.276657, 0.94497,    0.704217, 0.687329, 0.168174, 0.742479, 0.89843, 0.924235,    0.187823, 0.637878, 0.137488, 0.650883, 0.368604, 0.891039,    0.657748, 0.422126,    0.519376}}, {-1.3911, -0.0446626, -0.12325}}, {{2, {0.184206,    0.0178143, 0.0505738, 0.113455, 0.255197},   0.535158, {0.903731, 0.836799, 0.310228, 0.830941,    0.216402}, {0.668625, 0.567749, 0.932511, 0.292167, 0.480802,    0.929871, 0.795023, 0.641283, 0.112198, 0.0388321, 0.137275,    0.219157, 0.592823, 0.854626, 0.119461, 0.168583, 0.479367,    0.599429, 0.584303,    0.264852}}, {-1.45084, -0.076412, -0.10056}}, {{2, {0.642569,    0.289201, 0.753362, 0.0484508, 0.973944},   0.721452, {0.820851, 0.756284, 0.493142, 0.791581,    0.0258276}, {0.115001, 0.380943, 0.752749, 0.888552, 0.895844,    0.788121, 0.898123, 0.769091, 0.72726, 0.308754, 0.298694,    0.184788, 0.462408, 0.666185, 0.00949314, 0.431426, 0.413957,    0.692241, 0.288041, 0.610575}}, {-1.38285,   0.000430718, -0.00545415}}, {{2, {0.657673, 0.1991, 0.496459,    0.584748, 0.542672},   0.818156, {0.74371, 0.696195, 0.646828, 0.0300352,    0.845587}, {0.927104, 0.919568, 0.721282, 0.546893, 0.742316,    0.45716, 0.0550967, 0.5374, 0.31089, 0.0432035, 0.362855,    0.249359, 0.700315, 0.385531, 0.163756, 0.752899, 0.115567,    0.842859, 0.3456, 0.0091892}}, {-1.28895, 0.068245,   0.011658}}, {{2, {0.419372, 0.19603, 0.315565, 0.163602, 0.492268},    0.276462, {0.594283, 0.616709, 0.749951, 0.819301,    0.539186}, {0.0793098, 0.439061, 0.776098, 0.176331, 0.829951,    0.738746, 0.390567, 0.012575, 0.0770515, 0.623178, 0.547709,    0.666975, 0.0678623, 0.203806, 0.351679, 0.351411, 0.90426,    0.711539, 0.0752167,    0.757128}}, {-1.38745, -0.00387191, -0.00922056}}, {{2, {0.287551,     0.961588, 0.255915, 0.217941, 0.208241},   0.522527, {0.479818, 0.0416103, 0.37829, 0.783781,    0.0892504}, {0.0290353, 0.301239, 0.160603, 0.541542, 0.36206,    0.233376, 0.956797, 0.189863, 0.0106496, 0.329116, 0.245258,    0.114647, 0.253522, 0.0415654, 0.28367, 0.858731, 0.0355809,    0.833324, 0.761143,    0.378914}}, {-1.38823, -0.00921002, -0.0232452}}, {{2, {0.993971,    0.455034, 0.977361, 0.289663, 0.964935},   0.153796, {0.816758, 0.748121, 0.602875, 0.920419,    0.859962}, {0.558258, 0.592226, 0.591303, 0.614704, 0.443612,    0.338704, 0.549738, 0.331034, 0.58488, 0.303123, 0.716413,    0.569891, 0.205967, 0.309152, 0.261379, 0.59253, 0.916304,    0.344217, 0.107583, 0.775772}}, {-1.25723, 0.112095,   0.0786015}}, {{1, {0.168182, 0.741342, 0.187164, 0.91581,    0.609924},   0.149116, {0.595861, 0.301106, 0.166313, 0.810412,    0.0461229}, {0.970072, 0.581432, 0.50729, 0.329709, 0.40018,    0.375466, 0.198138, 0.0683302, 0.80765, 0.459162, 0.853921,    0.960747, 0.0318785, 0.29098, 0.112579, 0.773583, 0.116069,    0.681056, 0.963463,    0.177722}}, {-1.38904, -0.00701023, -0.0153209}}};

  for (unsigned int i = 0; i < sizeof (data) / sizeof (data[0]); ++i)
    {
      float dlogbeta;
      float ddlogbeta;

#define logafunc(n) data[i].inputs.loga[n]
#define cfunc(n) data[i].inputs.c[n]
#define dfunc(n, k) data[i].inputs.d[4 * n + k]
      float output = logprating (data[i].inputs.rating,
                                 logafunc,
                                 5,
                                 data[i].inputs.logbeta,
                                 dlogbeta,
                                 ddlogbeta,
                                 cfunc,
                                 dfunc,
                                 4);
#undef dfunc
#undef cfunc
#undef logafunc

      assert (fabs (output - data[i].desired_output.logp) <=
              1e-4 * (1 + fabs (output) + fabs (data[i].desired_output.logp)) ||
              (fprintf (stderr, "%g ?= %g\n", output, data[i].desired_output.logp),
               0));

      assert (fabs (dlogbeta - data[i].desired_output.dlogp) <=
              1e-4 * (1 + fabs (dlogbeta) + fabs (data[i].desired_output.dlogp)) ||
              (fprintf (stderr, "%g ?= %g\n", dlogbeta, data[i].desired_output.dlogp),
               0));

      assert (fabs (ddlogbeta - data[i].desired_output.ddlogp) <=
              1e-4 * (1 + fabs (ddlogbeta) + fabs (data[i].desired_output.ddlogp)) ||
              (fprintf (stderr, "%g ?= %g\n", ddlogbeta, data[i].desired_output.ddlogp),
               0));
    }
}

int 
main (void) 
{
  srand48 (69);

  test_logprating ();

  return 0;
}
